/*****************************
Last Modified: 2022/06/22
 *****************************/
#include "lflog.h"
#include <stdarg.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <libgen.h>

#define DEFAULT_FLOG_FILE   LFLOG_DIR"lflog.log"

struct __lflog{
    FILE *fd;
    char *logname;
    char *buf;
    LFLogLevel level;
};

static const char * levelName[LFLOG_MAX] = {
    "[NONE] ",
    "[DEBUG] ",
    "[INFO] ",
    "[WARNING] ",
    "[ERROR] ",
    "[CRITICAL] "
};

static int getcurtime(char* str)
{
    struct timeval t_val;
    struct tm* tm_l = NULL;
    time_t tim_t;
    size_t len = 0;
    char buf[8];

    gettimeofday(&t_val, NULL);
    tim_t = t_val.tv_sec;
    tm_l = localtime(&tim_t);
    len = strftime(str, 33, "%y/%m/%d %H:%M:%S.", tm_l);
    sprintf(buf, "%03d", (int)t_val.tv_usec/1000);
    strcat(str, buf);

    return len;
}

LFLog create_lflog(const char *logfile, LFLogLevel level)
{
    FILE *fd = NULL;
    LFLog log;

    log = calloc(sizeof(struct __lflog), 1);
    if(NULL == log){
        printf("calloc err.\n");
        return NULL;
    }
    log->buf = malloc(MAX_FLOG_BUFFER_LEN);
    if(NULL == log->buf){
        printf("malloc log buf");
        free(log);
        return NULL;
    }
    log->buf[0] = '\0';

    if(NULL == logfile){
        printf("Use default log: %s\n", DEFAULT_FLOG_FILE);
        logfile = DEFAULT_FLOG_FILE;
    }

    fd = fopen(logfile, "wa");
    if(fd == NULL){
        printf("open '%s' err.\n", logfile);
        free(log->buf);
        free(log);
        return NULL;
    }
    log->fd = fd;
    log->logname = strdup(logfile);
    if(level <= LFLOG_NONE)
        log->level = LFLOG_DEBUG;
    else if(level >= LFLOG_MAX)
        log->level = LFLOG_CRITICAL;
    else
        log->level = level;

    lflog(log, LFLOG_INFO, __FILE__, __FUNCTION__, "Create log:\"%s\"", log->logname);
    return log;
}

void destroy_lflog(LFLog log)
{
    if(NULL == log) return;
    lflog(log, LFLOG_INFO, __FILE__, __FUNCTION__, "Destroy log:\"%s\"", log->logname);
#ifdef __linux__
    fflush(log->fd);
    fclose(log->fd);sync();
#else
    fflush(log->fd);
    fclose(log->fd);
#endif
    if(NULL != log->logname){
        free(log->logname);
    }
    if(NULL != log->buf){
        free(log->buf);
    }
    free(log);
}

void change_lflog_level(LFLog log, LFLogLevel level)
{
    if (level > LFLOG_NONE && level < LFLOG_MAX) {
        log->level = level;
    }
}

static void vllog(LFLog log, char *dest, LFLogLevel level, const char *file, const char *function)
{
    char *bname = NULL;

    (void)log;
    getcurtime(dest);
    strcat(dest, levelName[level]);
    if(NULL != file){
        char *ft = strdup(file);
        bname = basename(ft);
        if(NULL != bname){
            strcat(dest, bname);
        }
        free(ft);
    }
    strcat(dest, ":");
    if(NULL != function){
        strcat(dest, function);
    }
    strcat(dest, ">> ");
}

void lflog(LFLog log, LFLogLevel level, const char *file, const char *function, const char *fmt, ...)
{
    va_list args;
    char *log_str;
    int len;

    if(NULL == log) return;
    if(log->level == LFLOG_NONE || level < log->level){
        return;
    }
    log_str = log->buf;log_str[0] = '\0';
    vllog(log, log_str, level, file, function);

    len = strlen(log_str);
    va_start(args, fmt);
    vsnprintf(log_str+len, MAX_FLOG_BUFFER_LEN - len, fmt, args);
    va_end(args);

    fwrite(log_str, strlen(log_str), 1, log->fd);
    fwrite("\n", 1, 1, log->fd);
}
